home *** CD-ROM | disk | FTP | other *** search
- /* TrackDisplayScheduling.c */
- /*****************************************************************************/
- /* */
- /* Out Of Phase: Digital Music Synthesis on General Purpose Computers */
- /* Copyright (C) 1994 Thomas R. Lawrence */
- /* */
- /* This program is free software; you can redistribute it and/or modify */
- /* it under the terms of the GNU General Public License as published by */
- /* the Free Software Foundation; either version 2 of the License, or */
- /* (at your option) any later version. */
- /* */
- /* This program is distributed in the hope that it will be useful, */
- /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
- /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
- /* GNU General Public License for more details. */
- /* */
- /* You should have received a copy of the GNU General Public License */
- /* along with this program; if not, write to the Free Software */
- /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- /* */
- /* Thomas R. Lawrence can be reached at tomlaw@world.std.com. */
- /* */
- /*****************************************************************************/
-
- #include "MiscInfo.h"
- #include "Audit.h"
- #include "Debug.h"
- #include "Definitions.h"
-
- #include "TrackDisplayScheduling.h"
- #include "TrackObject.h"
- #include "FrameObject.h"
- #include "Memory.h"
- #include "Fractions.h"
- #include "DataMunging.h"
- #include "NoteObject.h"
- #include "TieTrackPixel.h"
- #include "TieTracking.h"
- #include "StaffCalibration.h"
-
-
- /* where does the very first note start (to allow insertion before it) */
- #define FIRSTNOTESTART (32)
-
- typedef struct
- {
- long PixelStart;
- OrdType Width : 15; /* Well... >I< know it's not going to be big... */
- unsigned int SquashThisOne : 1; /* boolean; True == don't draw this one */
- } FrameAttrRec;
-
- typedef struct
- {
- TrackObjectRec* TrackObj;
- long NumFrames;
- FrameAttrRec* FrameAttrArray;
- } TrackAttrRec;
-
- typedef struct
- {
- short BarIndex : 15; /* -1 == no bar */
- unsigned int DrawBarGreyed; /* boolean; True == draw bar greyed */
- } MeasureBarInfoRec;
-
- struct TrackDispScheduleRec
- {
- long NumTracks;
- /* the first track (TrackAttrArray[0]) is the "main" track */
- TrackAttrRec* TrackAttrArray;
- MeasureBarInfoRec* MainTrackMeasureBars;
- long TotalWidth;
- MyBoolean RecalculationRequired;
- TieTrackPixelRec* TiePixelTracker; /* NIL if not up to date */
- };
-
-
- /* create a new schedule state record */
- TrackDispScheduleRec* NewTrackDisplaySchedule(struct TrackObjectRec* MainTrackObj)
- {
- TrackDispScheduleRec* Schedule;
-
- Schedule = (TrackDispScheduleRec*)AllocPtrCanFail(sizeof(TrackDispScheduleRec),
- "TrackDispScheduleRec");
- if (Schedule == NIL)
- {
- FailurePoint1:
- return NIL;
- }
- Schedule->TrackAttrArray = (TrackAttrRec*)AllocPtrCanFail(0,"TrackAttrRec");
- if (Schedule->TrackAttrArray == NIL)
- {
- FailurePoint2:
- ReleasePtr((char*)Schedule);
- goto FailurePoint1;
- }
- Schedule->MainTrackMeasureBars = (MeasureBarInfoRec*)AllocPtrCanFail(0,
- "MainTrackMeasureBars");
- if (Schedule->MainTrackMeasureBars == NIL)
- {
- FailurePoint3:
- ReleasePtr((char*)Schedule->TrackAttrArray);
- goto FailurePoint2;
- }
- Schedule->NumTracks = 0;
- Schedule->RecalculationRequired = True;
- Schedule->TiePixelTracker = NIL;
- if (!AddTrackToDisplaySchedule(Schedule,MainTrackObj))
- {
- FailurePoint4:
- ReleasePtr((char*)Schedule->MainTrackMeasureBars);
- goto FailurePoint3;
- }
- return Schedule;
- }
-
-
- /* dispose of schedule state record */
- void DisposeTrackDisplaySchedule(TrackDispScheduleRec* Schedule)
- {
- long Scan;
-
- CheckPtrExistence(Schedule);
- if (Schedule->TiePixelTracker != NIL)
- {
- DisposeTieTrackPixel(Schedule->TiePixelTracker);
- }
- for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
- {
- CheckPtrExistence(Schedule->TrackAttrArray[Scan].FrameAttrArray);
- ReleasePtr((char*)(Schedule->TrackAttrArray[Scan].FrameAttrArray));
- }
- ReleasePtr((char*)Schedule->MainTrackMeasureBars);
- ReleasePtr((char*)Schedule->TrackAttrArray);
- ReleasePtr((char*)Schedule);
- }
-
-
- /* add track to schedule list */
- MyBoolean AddTrackToDisplaySchedule(TrackDispScheduleRec* Schedule,
- struct TrackObjectRec* TrackObj)
- {
- TrackAttrRec* AttrList;
-
- CheckPtrExistence(Schedule);
- CheckPtrExistence(TrackObj);
- #if DEBUG
- {
- long Scan;
-
- for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
- {
- if (Schedule->TrackAttrArray[Scan].TrackObj == TrackObj)
- {
- PRERR(ForceAbort,"AddTrackToDisplaySchedule: track already in list");
- }
- }
- }
- #endif
- AttrList = (TrackAttrRec*)ResizePtr((char*)Schedule->TrackAttrArray,
- (Schedule->NumTracks + 1) * sizeof(TrackAttrRec));
- if (AttrList == NIL)
- {
- FailurePoint1:
- return False;
- }
- Schedule->TrackAttrArray = AttrList;
- Schedule->TrackAttrArray[Schedule->NumTracks].TrackObj = TrackObj;
- Schedule->TrackAttrArray[Schedule->NumTracks].NumFrames = 0;
- Schedule->TrackAttrArray[Schedule->NumTracks].FrameAttrArray
- = (FrameAttrRec*)AllocPtrCanFail(0,"FrameAttrRec");
- if (Schedule->TrackAttrArray[Schedule->NumTracks].FrameAttrArray == NIL)
- {
- goto FailurePoint1;
- }
- Schedule->NumTracks += 1;
- Schedule->RecalculationRequired = True;
- return True;
- }
-
-
- /* delete a track from the schedule list */
- void DeleteTrackFromDisplaySchedule(TrackDispScheduleRec* Schedule,
- struct TrackObjectRec* TrackObj)
- {
- long Index;
-
- CheckPtrExistence(Schedule);
- CheckPtrExistence(TrackObj);
- Index = 0;
- /* find the entry being deleted */
- while (Schedule->TrackAttrArray[Index].TrackObj != TrackObj)
- {
- /* this bounds check is actually after the fact, for the conditional above */
- PRNGCHK(Schedule->TrackAttrArray,&(Schedule->TrackAttrArray[Index]),
- sizeof(Schedule->TrackAttrArray[Index]));
- Index += 1;
- ERROR(Index >= Schedule->NumTracks,PRERR(ForceAbort,
- "DeleteTrackFromDisplaySchedule: track unknown"));
- }
- ERROR(Index == 0,PRERR(ForceAbort,
- "DeleteTrackFromDisplaySchedule: deleting the main track"));
- /* delete the actual array */
- ReleasePtr((char*)Schedule->TrackAttrArray[Index].FrameAttrArray);
- /* shift the elements in the array after the hole down. This will leave */
- /* empty slots at the end of the array but... who cares? */
- while (Index < Schedule->NumTracks - 1)
- {
- Schedule->TrackAttrArray[Index] = Schedule->TrackAttrArray[Index + 1];
- Index += 1;
- }
- /* decrement the extents counter */
- Schedule->NumTracks -= 1;
- /* mark thing ready for total recalculation */
- Schedule->RecalculationRequired = True;
- }
-
-
- /* note location information must be available by the time this is called. */
- static void BuildTieRepresentation(TrackDispScheduleRec* Schedule)
- {
- long FrameLimit;
- long FrameScan;
- TrackObjectRec* TrackObj;
- TieTrackRec* TieTracker;
-
- CheckPtrExistence(Schedule);
- PRNGCHK(Schedule->TrackAttrArray,&(Schedule->TrackAttrArray[0]),
- sizeof(Schedule->TrackAttrArray[0]));
- TrackObj = Schedule->TrackAttrArray[0].TrackObj;
- CheckPtrExistence(TrackObj);
- ERROR(Schedule->TiePixelTracker == NIL,PRERR(ForceAbort,
- "BuildTieRepresentation: TiePixelTracker is NIL"));
- CheckPtrExistence(Schedule->TiePixelTracker);
-
- TieTracker = NewTieTracker();
- if (TieTracker == NIL)
- {
- /* oh, well, all the ties just disappeared. */
- return;
- }
-
- FrameLimit = TrackObjectGetNumFrames(TrackObj);
- for (FrameScan = 0; FrameScan < FrameLimit; FrameScan += 1)
- {
- FrameObjectRec* Frame;
-
- Frame = TrackObjectGetFrame(TrackObj,FrameScan);
- if (!IsThisACommandFrame(Frame))
- {
- long NoteScan;
- long NoteLimit;
-
- NoteLimit = NumNotesInFrame(Frame);
- for (NoteScan = 0; NoteScan < NoteLimit; NoteScan += 1)
- {
- NoteObjectRec* Note;
- NoteObjectRec* TieTargetNote;
- MyBoolean ThereIsATieToThisNote;
- long PreviousTiePixelX;
- long PreviousTiePixelY;
- NoteObjectRec* PreviousTieNote;
-
- Note = GetNoteFromFrame(Frame,NoteScan);
-
- /* get any note it ties to */
- TieTargetNote = GetNoteTieTarget(Note);
-
- /* get any note tied to it */
- ThereIsATieToThisNote = GetTieSourceFromDestination(TieTracker,
- &PreviousTiePixelX,&PreviousTiePixelY,&PreviousTieNote,Note);
-
- /* now, deal with all of this jucy information */
- /* first, get the location of this note, if needed */
- if ((TieTargetNote != NIL) || ThereIsATieToThisNote)
- {
- long ThisNotePixelX;
- long ThisNotePixelY;
- EXECUTE(MyBoolean CallResult;)
-
- /* gather location information */
- EXECUTE(CallResult = )TrackDisplayIndexToPixel(Schedule,
- 0/*main track*/,FrameScan,&ThisNotePixelX);
- ThisNotePixelX += NoteScan * INTERNALSEPARATION;
- ERROR(!CallResult,PRERR(ForceAbort,
- "BuildTieRepresentation: TrackDisplayIndexToPixel failed"));
- ThisNotePixelY = ConvertPitchToPixel(GetNotePitch(Note),
- GetNoteFlatOrSharpStatus(Note));
-
- /* do the thing for a note tied to us */
- if (ThereIsATieToThisNote)
- {
- AddTieTrackPixelElement(Schedule->TiePixelTracker,
- PreviousTiePixelX,PreviousTiePixelY,ThisNotePixelX,
- ThisNotePixelY);
- }
-
- /* do the thing for us tied to another note */
- if (TieTargetNote != NIL)
- {
- AddTiePairToTieTracker(TieTracker,Note,
- ThisNotePixelX,ThisNotePixelY,TieTargetNote);
- }
- }
- }
- }
- }
-
- DisposeTieTracker(TieTracker);
- }
-
-
- typedef struct
- {
- long CurrentIndex;
- FractionRec CurrentFrameStartTime;
- FractionRec CurrentFrameDuration;
- } TrackWorkRec;
-
-
- #ifdef THINK_C
- /* THINK C's optimizer has some peculiar problems with overlaying variables */
- /* and using the wrong size to access compiler temporaries when compiling */
- /* this routine. See the bit where MaximumWidth is adjusted if it is less */
- /* than the width of the frame being considered. */
- #if __option(global_optimizer)
- #define THINK_C_GLOBAL_OPTIMIZER_ENABLED (1)
- #pragma options(!global_optimizer)
- #else
- #define THINK_C_GLOBAL_OPTIMIZER_ENABLED (0)
- #endif
- #endif
-
-
- /* apply schedule to tracks. */
- MyBoolean TrackDisplayScheduleUpdate(TrackDispScheduleRec* Schedule)
- {
- long Scan;
- long CurrentXLocation;
- FractionRec CurrentTime;
- TrackWorkRec* WorkArray;
- MyBoolean DoneFlag;
- MeasureBarInfoRec* NewMeasureBarArray;
- FractionRec MeasureBarIntervalThing;
- FractionRec MeasureBarWidth;
- long MeasureBarIndex;
-
- CheckPtrExistence(Schedule);
-
- /* if everything is up to date, then don't bother */
- if (!Schedule->RecalculationRequired)
- {
- return True;
- }
-
- if (Schedule->TiePixelTracker != NIL)
- {
- DisposeTieTrackPixel(Schedule->TiePixelTracker);
- Schedule->TiePixelTracker = NIL;
- }
- Schedule->TiePixelTracker = NewTieTrackPixel();
- if (Schedule->TiePixelTracker == NIL)
- {
- return False;
- }
-
- /* resize the measure bar table */
- Scan = TrackObjectGetNumFrames(Schedule->TrackAttrArray[0].TrackObj);
- NewMeasureBarArray = (MeasureBarInfoRec*)ResizePtr(
- (char*)Schedule->MainTrackMeasureBars,sizeof(MeasureBarInfoRec) * Scan);
- if (NewMeasureBarArray == NIL)
- {
- return False;
- }
- Schedule->MainTrackMeasureBars = NewMeasureBarArray;
- while (Scan > 0)
- {
- /* erase the entries in the table */
- Scan -= 1;
- Schedule->MainTrackMeasureBars[Scan].BarIndex = -1; /* no bar */
- }
-
- /* resize the track location tables */
- for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
- {
- long TheirFramesTemp;
-
- TheirFramesTemp = TrackObjectGetNumFrames(
- Schedule->TrackAttrArray[Scan].TrackObj);
- if (Schedule->TrackAttrArray[Scan].NumFrames != TheirFramesTemp)
- {
- FrameAttrRec* NewArray;
- long Index;
-
- /* resize the array */
- PRNGCHK(Schedule->TrackAttrArray,&(Schedule->TrackAttrArray[Scan]),
- sizeof(Schedule->TrackAttrArray[Scan]));
- CheckPtrExistence(Schedule->TrackAttrArray[Scan].FrameAttrArray);
- NewArray = (FrameAttrRec*)ResizePtr((char*)(Schedule->TrackAttrArray
- [Scan].FrameAttrArray),sizeof(FrameAttrRec) * TheirFramesTemp);
- if (NewArray == NIL)
- {
- return False;
- }
- Schedule->TrackAttrArray[Scan].FrameAttrArray = NewArray;
-
- /* zero the new space in the array so that we make sure it changes */
- for (Index = Schedule->TrackAttrArray[Scan].NumFrames;
- Index < TheirFramesTemp; Index += 1)
- {
- PRNGCHK(Schedule->TrackAttrArray[Scan].FrameAttrArray,
- &(Schedule->TrackAttrArray[Scan].FrameAttrArray[Index]),
- sizeof(Schedule->TrackAttrArray[Scan].FrameAttrArray[Index]));
- Schedule->TrackAttrArray[Scan].FrameAttrArray[Index].PixelStart = -1;
- Schedule->TrackAttrArray[Scan].FrameAttrArray[Index].Width = -1;
- Schedule->TrackAttrArray[Scan].FrameAttrArray[Index].SquashThisOne = False;
- }
-
- /* update size number */
- Schedule->TrackAttrArray[Scan].NumFrames = TheirFramesTemp;
- }
- }
-
-
- /* initialize local variable counters */
- CurrentXLocation = FIRSTNOTESTART;
- CurrentTime.Integer = 0;
- CurrentTime.Fraction = 0;
- CurrentTime.Denominator = (64*3*5*7*2);
-
- MeasureBarIntervalThing.Integer = 0;
- MeasureBarIntervalThing.Fraction = 0;
- MeasureBarIntervalThing.Denominator = (64*3*5*7*2);
- MeasureBarWidth.Integer = 1;
- MeasureBarWidth.Fraction = 0;
- MeasureBarWidth.Denominator = (64*3*5*7*2);
- MeasureBarIndex = 1;
-
-
- /* build workspace for each track */
- WorkArray = (TrackWorkRec*)AllocPtrCanFail(sizeof(TrackWorkRec)
- * Schedule->NumTracks,"TrackWorkRec");
- if (WorkArray == NIL)
- {
- return False;
- }
- for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
- {
- WorkArray[Scan].CurrentIndex = 0;
- WorkArray[Scan].CurrentFrameStartTime.Integer = 0;
- WorkArray[Scan].CurrentFrameStartTime.Fraction = 0;
- WorkArray[Scan].CurrentFrameStartTime.Denominator = (64*3*5*7*2);
- }
-
-
- /* perform scheduling sweep */
- /* how it works: */
- /* the start time and duration of the next frame in each channel are */
- /* calculated. If the start time is NOT in the future, but is now, then */
- /* the note can be scheduled. When it is scheduled, it's start time is */
- /* advanced by adding it's duration. This way, notes will be scheduled */
- /* for display on the screen in the same order that they will be played. */
- do
- {
- long MaximumWidth;
-
-
- /* stage 1: calculate duration of next event for all frames */
- /* This also figures out what the smallest duration is */
- /* After stage 1, Frame will be NIL if the channel is not scheduled */
- /* this time around, or valid if it contains the one to schedule */
- MaximumWidth = 0;
- for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
- {
-
- if (WorkArray[Scan].CurrentIndex
- < Schedule->TrackAttrArray[Scan].NumFrames)
- {
- /* this channel still has more items to schedule. */
- /* we want to see if this frame will be scheduled this time. */
- ERROR(FracGreaterThan(&CurrentTime,&(WorkArray[Scan]
- .CurrentFrameStartTime)),PRERR(AllowResume,
- "TrackDisplayScheduleUpdate: current time later than frame start"));
-
- if (FractionsEqual(&CurrentTime,&(WorkArray[Scan].CurrentFrameStartTime)))
- {
- FrameObjectRec* Frame;
-
- /* the frame is scheduled this time around */
-
- /* obtain the frame reference */
- Frame = TrackObjectGetFrame(Schedule->
- TrackAttrArray[Scan].TrackObj,WorkArray[Scan].CurrentIndex);
-
- /* get duration of this frame */
- DurationOfFrame(Frame,&(WorkArray[Scan].CurrentFrameDuration));
-
- /* decide whether the measure bar flag should be set. */
- /* this is only done for main track, hence "Scan == 0". */
- if (Scan == 0)
- {
- /* for big notes (like quads) this may increment */
- /* MeasureBarIndex several times, but that's the desired */
- /* behavior. */
- while (FracGreaterEqual(&MeasureBarIntervalThing,
- &MeasureBarWidth))
- {
- MyBoolean GreyedFlag;
-
- /* if the current measure bar index is greater than the */
- /* measure bar interval, then set the flag & decrement */
- /* the measure bar interval. */
- /* MeasureBarWidth = num whole notes in a measure */
- /* MeasureBarIntervalThing = current accumulated notes */
- GreyedFlag = !FractionsEqual(&MeasureBarIntervalThing,
- &MeasureBarWidth);
- SubFractions(&MeasureBarIntervalThing,&MeasureBarWidth,
- &MeasureBarIntervalThing);
- PRNGCHK(Schedule->MainTrackMeasureBars,&(Schedule
- ->MainTrackMeasureBars[WorkArray[Scan].CurrentIndex]),
- sizeof(Schedule->MainTrackMeasureBars[WorkArray[Scan]
- .CurrentIndex]));
- Schedule->MainTrackMeasureBars[WorkArray[Scan]
- .CurrentIndex].BarIndex = MeasureBarIndex;
- Schedule->MainTrackMeasureBars[WorkArray[Scan]
- .CurrentIndex].DrawBarGreyed = GreyedFlag;
- MeasureBarIndex += 1;
- }
-
- /* now, increment the value with the current note */
- AddFractions(&MeasureBarIntervalThing,&(WorkArray[Scan]
- .CurrentFrameDuration),&MeasureBarIntervalThing);
-
- /* finally, if the note is a meter adjust command, use */
- /* it to recalibrate the measure barring */
- if (IsThisACommandFrame(Frame))
- {
- NoteObjectRec* MaybeMeterCmd;
-
- MaybeMeterCmd = GetNoteFromFrame(Frame,0);
- if (GetCommandOpcode(MaybeMeterCmd) == eCmdSetMeter)
- {
- /* set the meter. this is used by the editor for */
- /* placing measure bars. measuring restarts */
- /* immediately after this command */
- /* <1i> = numerator, <2i> = denominator */
- if (GetCommandNumericArg2(MaybeMeterCmd) >= 1)
- {
- /* make sure denominator is within range */
- if (GetCommandNumericArg1(MaybeMeterCmd) >= 1)
- {
- /* set the new measure width */
- MeasureBarWidth.Denominator
- = GetCommandNumericArg2(MaybeMeterCmd);
- MeasureBarWidth.Fraction
- = GetCommandNumericArg1(MaybeMeterCmd)
- % MeasureBarWidth.Denominator;
- MeasureBarWidth.Integer
- = GetCommandNumericArg1(MaybeMeterCmd)
- / MeasureBarWidth.Denominator;
-
- /* reset the current index */
- MeasureBarIntervalThing.Denominator = (64*3*5*7*2);
- MeasureBarIntervalThing.Fraction = 0;
- MeasureBarIntervalThing.Integer = 0;
- }
- }
- }
- else if (GetCommandOpcode(MaybeMeterCmd)
- == eCmdSetMeasureNumber)
- {
- MeasureBarIndex = GetCommandNumericArg1(MaybeMeterCmd);
- }
- }
- }
-
- /* now that we have the frame's duration and have it scheduled */
- /* for display (Frame), increment the start */
- /* time to the end of the frame (which is start of next frame) */
- PRNGCHK(WorkArray,&(WorkArray[Scan]),sizeof(WorkArray[Scan]));
- AddFractions(&(WorkArray[Scan].CurrentFrameDuration),
- &(WorkArray[Scan].CurrentFrameStartTime),
- &(WorkArray[Scan].CurrentFrameStartTime));
-
- if ((Scan == 0) || !IsThisACommandFrame(Frame))
- {
- /* if this is NOT a command frame, then it should be scheduled */
- /* normally for display. */
-
- /* set up the drawing attributes */
- PRNGCHK(Schedule->TrackAttrArray[Scan].FrameAttrArray,
- &(Schedule->TrackAttrArray[Scan].FrameAttrArray[WorkArray[Scan]
- .CurrentIndex]),sizeof(Schedule->TrackAttrArray[Scan]
- .FrameAttrArray[WorkArray[Scan].CurrentIndex]));
-
- Schedule->TrackAttrArray[Scan].FrameAttrArray[
- WorkArray[Scan].CurrentIndex].PixelStart = CurrentXLocation;
- Schedule->TrackAttrArray[Scan].FrameAttrArray[
- WorkArray[Scan].CurrentIndex].Width = WidthOfFrameAndDraw(
- 0,0,0,0,0,0,Frame,False/*don'tdraw*/,False);
- Schedule->TrackAttrArray[Scan].FrameAttrArray[
- WorkArray[Scan].CurrentIndex].SquashThisOne = False;
-
- ERROR(Schedule->TrackAttrArray[Scan].FrameAttrArray[
- WorkArray[Scan].CurrentIndex].Width == 0,PRERR(AllowResume,
- "TrackDisplayScheduleUpdate: frame's width is 0"));
-
- /* we need to obtain the maximum width of these frame so we */
- /* can figure out what the next X location will be */
- if (Schedule->TrackAttrArray[Scan].FrameAttrArray[
- WorkArray[Scan].CurrentIndex].Width > MaximumWidth)
- {
- MaximumWidth = Schedule->TrackAttrArray[Scan]
- .FrameAttrArray[WorkArray[Scan].CurrentIndex].Width;
- }
- }
- else
- {
- /* otherwise this IS a command frame in a non-main track */
- /* and therefore it should not be scheduled */
-
- /* adjust the displaylocation parameters */
- PRNGCHK(Schedule->TrackAttrArray[Scan].FrameAttrArray,
- &(Schedule->TrackAttrArray[Scan].FrameAttrArray[WorkArray[Scan]
- .CurrentIndex]),sizeof(Schedule->TrackAttrArray[Scan]
- .FrameAttrArray[WorkArray[Scan].CurrentIndex]));
-
- Schedule->TrackAttrArray[Scan].FrameAttrArray[
- WorkArray[Scan].CurrentIndex].PixelStart = CurrentXLocation;
- Schedule->TrackAttrArray[Scan].FrameAttrArray[
- WorkArray[Scan].CurrentIndex].Width = 0; /* no width! */
- Schedule->TrackAttrArray[Scan].FrameAttrArray[
- WorkArray[Scan].CurrentIndex].SquashThisOne = True; /*squish*/
- }
-
- /* advance frame pointer to the next frame in the channel */
- WorkArray[Scan].CurrentIndex += 1;
- }
- }
- }
- /* advance current X position using biggest width at this position */
- if (MaximumWidth != 0)
- {
- /* MaximumWidth != 0 only happens if some stuff was scheduled */
- CurrentXLocation += MaximumWidth + EXTERNALSEPARATION;
- }
-
-
- /* stage 2: see if we are totally done with the tracks */
- DoneFlag = True;
- for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
- {
- if (WorkArray[Scan].CurrentIndex
- < Schedule->TrackAttrArray[Scan].NumFrames)
- {
- /* this channel still has more items to schedule. */
- DoneFlag = False;
- }
- }
-
-
- /* stage 3: advance the current time counter to point to the start time */
- /* of the closest possible next frame, which corresponds to the soonest one */
- /* immediately after the smallest frame that we just scheduled. */
- /* if we aren't done, figure out when the soonest next frame starts */
- if (!DoneFlag)
- {
- FractionRec SmallestDelta;
- MyBoolean SmallestDeltaValid;
-
- /* but there's still more to go, so advance the time counter to */
- /* the closest possible next frame. */
- /* This will happen if a very small note ends a track. The duration */
- /* of the small note will be added, but since the track is done, */
- /* there might not be any note whatsoever at that time. In this */
- /* case, we need to find when the next note will be. */
- /* In order to do this, we search for the soonest possible frame */
- /* that we can find that is past CurrentTime. */
- SmallestDeltaValid = False;
- for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
- {
- if (WorkArray[Scan].CurrentIndex
- < Schedule->TrackAttrArray[Scan].NumFrames)
- {
- FractionRec DeltaTemp;
-
- /* start time of next frame should NEVER be less than */
- /* current time. */
- ERROR(FracGreaterEqual(&CurrentTime,&(WorkArray[Scan]
- .CurrentFrameStartTime)) && !FractionsEqual(&CurrentTime,
- &(WorkArray[Scan].CurrentFrameStartTime)),PRERR(ForceAbort,
- "TrackDisplayScheduleUpdate: next less than current time"));
-
- /* figure out difference between then and now */
- SubFractions(&(WorkArray[Scan].CurrentFrameStartTime),
- &CurrentTime,&DeltaTemp);
- if (!SmallestDeltaValid
- || FracGreaterThan(&SmallestDelta,&DeltaTemp))
- {
- SmallestDelta = DeltaTemp;
- SmallestDeltaValid = True;
- }
- }
- }
-
-
- /* if SmallestDeltaValid is False at this point, then there aren't */
- /* any things that can be done, which should never happen. */
- ERROR(!SmallestDeltaValid,PRERR(AllowResume,
- "TrackDisplayScheduleUpdate: no track, but DoneFlag is false"));
- AddFractions(&SmallestDelta,&CurrentTime,&CurrentTime);
- }
-
- /* make sure things don't get out of hand. this magic number is derived */
- /* from the denominator of the smallest possible note: 64th note, with */
- /* a possible 3, 5, or 7 division, and (3/2) dot. */
- ERROR(CurrentTime.Denominator > 64*3*5*7*2,PRERR(ForceAbort,
- "TrackDisplayScheduleUpdate: factoring malfunction"));
- } while (!DoneFlag);
- Schedule->TotalWidth = CurrentXLocation;
- Schedule->RecalculationRequired = False;
- ReleasePtr((char*)WorkArray);
-
- /* build tie representation */
- BuildTieRepresentation(Schedule);
-
- return True;
- }
-
-
- #ifdef THINK_C
- #if THINK_C_GLOBAL_OPTIMIZER_ENABLED
- #pragma options(global_optimizer)
- #endif
- #endif
-
-
- /* find out where a pixel position is located in the score. it returns the */
- /* index of the frame the position is on and *OnFrame is True if it was on a frame. */
- /* otherwise, it returns the index of the frame that the position precedes and */
- /* sets *OnFrame to False. TrackObj is the track that we want to find positions */
- /* with respect to. */
- MyBoolean TrackDisplayPixelToIndex(TrackDispScheduleRec* Schedule,
- struct TrackObjectRec* TrackObj, long PixelPosition,
- MyBoolean* OnFrame, long* Index)
- {
- FrameAttrRec* FrameAttrArray;
- long Scan;
- long Limit;
- long LeftIndex;
- long RightIndex;
-
- CheckPtrExistence(Schedule);
- CheckPtrExistence(TrackObj);
- if (!TrackDisplayScheduleUpdate(Schedule))
- {
- return False;
- }
- for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
- {
- if (TrackObj == Schedule->TrackAttrArray[Scan].TrackObj)
- {
- FrameAttrArray = Schedule->TrackAttrArray[Scan].FrameAttrArray;
- CheckPtrExistence(FrameAttrArray);
- Limit = Schedule->TrackAttrArray[Scan].NumFrames;
- goto FoundTrackPoint;
- }
- }
- EXECUTE(PRERR(ForceAbort,"TrackDisplayPixelToIndex: unknown track specified"));
- FoundTrackPoint:
- /* perform a binary search to locate the cell responsible for the track */
- /* first, check to make sure index is within range */
- if (PixelPosition < 0)
- {
- *OnFrame = False;
- *Index = 0;
- return True;
- }
- if ((Limit == 0) || (PixelPosition >= FrameAttrArray[Limit - 1].PixelStart
- + FrameAttrArray[Limit - 1].Width))
- {
- /* since we are beyond the end, we set *OnFrame false to indicate that */
- /* the pixel position given is not a valid frame, and we return the index */
- /* of the [nonexistent] next frame */
- *OnFrame = False;
- *Index = Limit;
- return True;
- }
- /* initialize counter variables. Invariant: Limit > 0 */
- LeftIndex = 0;
- RightIndex = Limit - 1;
- while (LeftIndex != RightIndex)
- {
- long Midpoint;
-
- /* find out pivot point for comparison */
- Midpoint = (LeftIndex + RightIndex) / 2;
- /* check to see if index falls on pivot frame */
- PRNGCHK(FrameAttrArray,&(FrameAttrArray[Midpoint]),
- sizeof(FrameAttrArray[Midpoint]));
- if ((FrameAttrArray[Midpoint].PixelStart <= PixelPosition)
- && (FrameAttrArray[Midpoint].PixelStart
- + FrameAttrArray[Midpoint].Width > PixelPosition))
- {
- *OnFrame = True;
- *Index = Midpoint;
- return True;
- }
- /* check to see if we can discard left half */
- if (FrameAttrArray[Midpoint].PixelStart <= PixelPosition)
- {
- LeftIndex = Midpoint + 1;
- }
- /* check to see if we can discard right half */
- if (FrameAttrArray[Midpoint].PixelStart > PixelPosition)
- {
- RightIndex = Midpoint;
- }
- }
- /* invariant: LeftIndex == RightIndex */
- /* now figure out what the results mean */
- if (FrameAttrArray[LeftIndex].PixelStart > PixelPosition)
- {
- /* it is before the one we found, in the interstice. */
- *OnFrame = False;
- *Index = LeftIndex;
- return True;
- }
- /* otherwise, it must be on the one we found */
- ERROR(FrameAttrArray[LeftIndex].PixelStart + FrameAttrArray[LeftIndex].Width
- < PixelPosition,PRERR(ForceAbort,"TrackDisplayPixelToIndex: error"));
- *OnFrame = True;
- *Index = LeftIndex;
- return True;
- }
-
-
- /* mark scheduler so that it recalculates all the stuff. the track and frame */
- /* index specify where recalculation has to start from, so that data before that */
- /* doesn't need to be updated. */
- void TrackDisplayScheduleMarkChanged(TrackDispScheduleRec* Schedule,
- struct TrackObjectRec* TrackObj, long FrameIndex)
- {
- CheckPtrExistence(Schedule);
- CheckPtrExistence(TrackObj);
- /* we ignore starting position parameters for now. */
- Schedule->RecalculationRequired = True;
- }
-
-
- /* calculate the pixel index for a frame based on it's array index */
- MyBoolean TrackDisplayIndexToPixel(TrackDispScheduleRec* Schedule,
- long TrackIndex, long FrameIndex, long* Pixel)
- {
- CheckPtrExistence(Schedule);
- ERROR((TrackIndex < 0) || (TrackIndex >= TrackDisplayGetNumTracks(Schedule)),
- PRERR(ForceAbort,"TrackDisplayIndexToPixel: track index out of range"));
- if (!TrackDisplayScheduleUpdate(Schedule))
- {
- return False;
- }
- PRNGCHK(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray,
- &(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray[FrameIndex]),
- sizeof(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray[FrameIndex]));
- *Pixel = Schedule->TrackAttrArray[TrackIndex].FrameAttrArray[FrameIndex].PixelStart;
- return True;
- }
-
-
- /* get the number of tracks controlled by this scheduler */
- long TrackDisplayGetNumTracks(TrackDispScheduleRec* Schedule)
- {
- CheckPtrExistence(Schedule);
- return Schedule->NumTracks;
- }
-
-
- /* look up the track index of a specific track */
- long TrackDisplayGetTrackIndex(TrackDispScheduleRec* Schedule,
- struct TrackObjectRec* TrackObj)
- {
- long Scan;
-
- CheckPtrExistence(Schedule);
- Scan = 0;
- while (Scan < Schedule->NumTracks)
- {
- if (Schedule->TrackAttrArray[Scan].TrackObj == TrackObj)
- {
- return Scan;
- }
- Scan += 1;
- }
- EXECUTE(PRERR(ForceAbort,"TrackDisplayGetTrackIndex: unknown track"));
- }
-
-
- /* get the track specified by the index (0..last func return value - 1) */
- TrackObjectRec* TrackDisplayGetParticularTrack(TrackDispScheduleRec* Schedule,
- long Index)
- {
- CheckPtrExistence(Schedule);
- PRNGCHK(Schedule->TrackAttrArray,&(Schedule->TrackAttrArray[Index].TrackObj),
- sizeof(TrackObjectRec*));
- return Schedule->TrackAttrArray[Index].TrackObj;
- }
-
-
- /* get the total length of the track */
- MyBoolean TrackDisplayGetTotalLength(TrackDispScheduleRec* Schedule,
- long* LongestLength)
- {
- CheckPtrExistence(Schedule);
- if (!TrackDisplayScheduleUpdate(Schedule))
- {
- return False;
- }
- *LongestLength = Schedule->TotalWidth;
- return True;
- }
-
-
- /* find out what NOTE (not frame) is at the specified location. NIL */
- /* is returned if no note is there. If the flag is clear, then it's a note, */
- /* otherwise it's a command. If you intend to modify it, then pass an address */
- /* for FrameIndex so you know what to pass to TrackDisplayScheduleMarkChanged. */
- struct NoteObjectRec* TrackDisplayGetUnderlyingNote(TrackDispScheduleRec* Schedule,
- long TrackIndex, MyBoolean* CommandFlag, long PixelX,
- long* FrameIndex)
- {
- MyBoolean OnFrame;
- long Index;
- FrameObjectRec* Frame;
- long BeginningOfFrame;
- OrdType FrameWidth;
-
- CheckPtrExistence(Schedule);
- ERROR((TrackIndex < 0) || (TrackIndex >= TrackDisplayGetNumTracks(Schedule)),
- PRERR(ForceAbort,"TrackDisplayGetUnderlyingNote: track index out of range"));
- if (!TrackDisplayPixelToIndex(Schedule,Schedule->TrackAttrArray[TrackIndex].TrackObj,
- PixelX,&OnFrame,&Index))
- {
- *FrameIndex = 0;
- return NIL;
- }
- if (FrameIndex != NIL)
- {
- *FrameIndex = Index;
- }
- if (!OnFrame)
- {
- return NIL;
- }
- if (!TrackDisplayIndexToPixel(Schedule,TrackIndex,Index,&BeginningOfFrame))
- {
- return NIL;
- }
- Frame = TrackObjectGetFrame(Schedule->TrackAttrArray[TrackIndex].TrackObj,Index);
- FrameWidth = WidthOfFrameAndDraw(0,0,0,0,0,0,Frame,False/*nodraw*/,False);
- *CommandFlag = IsThisACommandFrame(Frame);
- if (*CommandFlag)
- {
- /* just a single command */
- if ((BeginningOfFrame <= PixelX) && (BeginningOfFrame + FrameWidth > PixelX))
- {
- return GetNoteFromFrame(Frame,0);
- }
- else
- {
- return NIL;
- }
- }
- else
- {
- /* notes are in the frame. */
- if ((BeginningOfFrame <= PixelX) && (BeginningOfFrame + FrameWidth > PixelX))
- {
- long NoteIndex;
- long NumFrames;
-
- /* calculate possible note index */
- NoteIndex = (PixelX - BeginningOfFrame) / INTERNALSEPARATION;
- ERROR(NumNotesInFrame(Frame) == 0,PRERR(ForceAbort,
- "TrackDisplayGetUnderlyingNote: frame doesn't contain any notes"));
- NumFrames = NumNotesInFrame(Frame);
- if (NoteIndex > NumFrames - 1)
- {
- NoteIndex = NumFrames - 1;
- }
- return GetNoteFromFrame(Frame,NoteIndex);
- }
- else
- {
- return NIL;
- }
- }
- }
-
-
- /* find out if a certain frame should be drawn. this is for squashing command */
- /* frames from channels other than the front channel */
- MyBoolean TrackDisplayShouldWeDrawIt(TrackDispScheduleRec* Schedule,
- long TrackIndex, long FrameIndex)
- {
- CheckPtrExistence(Schedule);
- ERROR((TrackIndex < 0) || (TrackIndex >= TrackDisplayGetNumTracks(Schedule)),
- PRERR(ForceAbort,"TrackDisplayShouldWeDrawIt: track index out of range"));
- if (!TrackDisplayScheduleUpdate(Schedule))
- {
- return False;
- }
- PRNGCHK(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray,
- &(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray[FrameIndex]),
- sizeof(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray[FrameIndex]));
- return !Schedule->TrackAttrArray[TrackIndex]
- .FrameAttrArray[FrameIndex].SquashThisOne;
- }
-
-
- /* find out what index a measure bar should be. if it shouldn't be a measure */
- /* bar, then it returns -1. */
- long TrackDisplayMeasureBarIndex(TrackDispScheduleRec* Schedule,
- long FrameIndex)
- {
- CheckPtrExistence(Schedule);
- if (!TrackDisplayScheduleUpdate(Schedule))
- {
- return -1;
- }
- PRNGCHK(Schedule->MainTrackMeasureBars,&(Schedule->MainTrackMeasureBars[FrameIndex]),
- sizeof(Schedule->MainTrackMeasureBars[FrameIndex]));
- return Schedule->MainTrackMeasureBars[FrameIndex].BarIndex;
- }
-
-
- /* find out the frame index for the specified measure bar. returns False if it */
- /* couldn't find that one */
- MyBoolean TrackDisplayMeasureIndexToFrame(TrackDispScheduleRec* Schedule,
- long MeasureBarIndex, long* FrameIndexOut)
- {
- long Scan;
-
- CheckPtrExistence(Schedule);
- if (!TrackDisplayScheduleUpdate(Schedule))
- {
- return False;
- }
- for (Scan = 0; Scan < Schedule->TrackAttrArray[0].NumFrames; Scan += 1)
- {
- PRNGCHK(Schedule->MainTrackMeasureBars,&(Schedule->MainTrackMeasureBars[Scan]),
- sizeof(Schedule->MainTrackMeasureBars[Scan]));
- if (Schedule->MainTrackMeasureBars[Scan].BarIndex == MeasureBarIndex)
- {
- *FrameIndexOut = Scan;
- return True;
- }
- }
- return False;
- }
-
-
- /* find out if a measure bar should be greyed out or drawn solid. it returns */
- /* true if the measure bar should be greyed. */
- MyBoolean TrackDisplayShouldMeasureBarBeGreyed(
- TrackDispScheduleRec* Schedule, long FrameIndex)
- {
- CheckPtrExistence(Schedule);
- if (!TrackDisplayScheduleUpdate(Schedule))
- {
- return False;
- }
- PRNGCHK(Schedule->MainTrackMeasureBars,&(Schedule->MainTrackMeasureBars[FrameIndex]),
- sizeof(Schedule->MainTrackMeasureBars[FrameIndex]));
- ERROR(Schedule->MainTrackMeasureBars[FrameIndex].BarIndex == -1,PRERR(AllowResume,
- "TrackDisplayShouldMeasureBarBeGreyed: no measure bar should be drawn here."));
- return Schedule->MainTrackMeasureBars[FrameIndex].DrawBarGreyed;
- }
-
-
- /* get a list of coordinates that need to be tied together */
- /* it might return NIL if there isn't a tie tracker. */
- struct TieIntersectListRec* TrackDisplayGetTieIntervalList(TrackDispScheduleRec* Schedule,
- long StartX, long Width)
- {
- TieIntersectListRec* TheList;
-
- CheckPtrExistence(Schedule);
- if (!TrackDisplayScheduleUpdate(Schedule))
- {
- return NIL;
- }
- if (Schedule->TiePixelTracker == NIL)
- {
- return NIL;
- }
- TheList = GetTieTrackPixelIntersecting(Schedule->TiePixelTracker,StartX,Width);
- return TheList;
- }
-